package com.enation.app.javashop.core.trade.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dag.eagleshop.core.account.model.dto.payment.UnfreezeCancelPayReqDTO;
import com.dag.eagleshop.core.account.model.dto.payment.UnfreezeCancelPayRespDTO;
import com.dag.eagleshop.core.account.model.enums.AccountTypeEnum;
import com.dag.eagleshop.core.account.service.AccountPaymentManager;
import com.dag.eagleshop.core.delivery.model.dto.JsonBean;
import com.dag.eagleshop.core.delivery.model.dto.UpdateOrderDTO;
import com.dag.eagleshop.core.delivery.service.DeliveryManager;
import com.enation.app.javashop.core.aftersale.AftersaleErrorCode;
import com.enation.app.javashop.core.base.CachePrefix;
import com.enation.app.javashop.core.base.context.Region;
import com.enation.app.javashop.core.base.message.OrderStatusChangeMsg;
import com.enation.app.javashop.core.base.rabbitmq.AmqpExchange;
import com.enation.app.javashop.core.geo.service.TencentManager;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.model.dos.MemberAddress;
import com.enation.app.javashop.core.member.service.MemberAddressManager;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.payment.model.dos.PaymentBillDO;
import com.enation.app.javashop.core.payment.model.enums.PaymentPluginEnum;
import com.enation.app.javashop.core.payment.model.enums.TradeType;
import com.enation.app.javashop.core.payment.service.PaymentBillManager;
import com.enation.app.javashop.core.member.model.dos.LeaderDO;
import com.enation.app.javashop.core.member.service.LeaderManager;
import com.enation.app.javashop.core.promotion.shetuan.model.dos.ShetuanGoodsDO;
import com.enation.app.javashop.core.promotion.shetuan.service.ShetuanGoodsManager;
import com.enation.app.javashop.core.shop.model.dos.ShopDetailDO;
import com.enation.app.javashop.core.shop.service.ShopManager;
import com.enation.app.javashop.core.trade.TradeErrorCode;
import com.enation.app.javashop.core.trade.cart.model.dos.OrderPermission;
import com.enation.app.javashop.core.trade.order.model.dos.OrderDO;
import com.enation.app.javashop.core.trade.order.model.dos.OrderItemsDO;
import com.enation.app.javashop.core.trade.order.model.dos.OrderLogDO;
import com.enation.app.javashop.core.trade.order.model.dos.TradeDO;
import com.enation.app.javashop.core.trade.order.model.enums.*;
import com.enation.app.javashop.core.trade.order.model.vo.*;
import com.enation.app.javashop.core.trade.order.service.*;
import com.enation.app.javashop.core.trade.order.support.OrderOperateChecker;
import com.enation.app.javashop.core.trade.order.support.OrderOperateFlow;
import com.enation.app.javashop.core.trade.order.support.OrderStep;
import com.enation.app.javashop.core.trade.sdk.model.OrderSkuDTO;
import com.enation.app.javashop.framework.context.AdminUserContext;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.exception.NoPermissionException;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Buyer;
import com.enation.app.javashop.framework.security.model.Seller;
import com.enation.app.javashop.framework.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 订单流程操作
 *
 * @author Snow create in 2018/5/15
 * @version v2.0
 * @since v7.0.0
 */
@Service
public class OrderOperateManagerImpl implements OrderOperateManager {

    protected final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    @Qualifier("memberDaoSupport")
    private DaoSupport memberDaoSupport;
    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;
    @Autowired
    private OrderQueryManager orderQueryManager;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private OrderLogManager orderLogManager;
    @Autowired
    private OrderManager orderManager;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TradeQueryManager tradeQueryManager;
    @Autowired
    private TradePriceManager tradePriceManager;
    @Autowired
    private OrderMetaManager orderMetaManager;
    @Autowired
    private PaymentBillManager paymentBillManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderProfitManager orderProfitManager;
    @Autowired
    private ShetuanGoodsManager shetuanGoodsManager;
    @Autowired
    private LeaderManager leaderManager;
    @Autowired
    private AccountPaymentManager accountPaymentManager;
    @Autowired
    private MemberManager memberManager;
    @Autowired
    private OrderOperateManager orderOperateManager;
    @Autowired
    private DeliveryManager deliveryManager;
    @Autowired
    private MemberAddressManager memberAddressManager;
    @Autowired
    private TencentManager tencentManager;
    @Autowired
    private ShopManager shopManager;


    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void confirm(ConfirmVO confirmVO, OrderPermission permission) {
        orderOperateManager.executeOperate(confirmVO.getOrderSn(), permission, OrderOperateEnum.CONFIRM, confirmVO);
    }


    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public OrderDO payOrder(String orderSn, Double payPrice, String returnTradeNo, OrderPermission permission) {

        PayParam payParam = new PayParam();
        payParam.setPayPrice(payPrice);
        payParam.setReturnTradeNo(returnTradeNo);

        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.PAY, payParam);
        return null;
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void ship(DeliveryVO deliveryVO, OrderPermission permission) {

        String orderSn = deliveryVO.getOrderSn();
        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.SHIP, deliveryVO);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void partShip(DeliveryVO deliveryVO, OrderPermission permission) {
        String orderSn = deliveryVO.getOrderSn();
        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.SPLIT_SHIP, deliveryVO);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void rog(RogVO rogVO, OrderPermission permission) {

        String orderSn = rogVO.getOrderSn();
        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.ROG, rogVO);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void cancel(CancelVO cancelVO, OrderPermission permission) {

        String orderSn = cancelVO.getOrderSn();
        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.CANCEL, cancelVO);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void complete(CompleteVO completeVO, OrderPermission permission) {
        String orderSn = completeVO.getOrderSn();
        orderOperateManager.executeOperate(orderSn, permission, OrderOperateEnum.COMPLETE, completeVO);
    }


    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateServiceStatus(String orderSn, ServiceStatusEnum serviceStatus) {
        String sql = "update es_order set service_status = ?  where sn = ? ";
        this.daoSupport.execute(sql, serviceStatus.value(), orderSn);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public OrderConsigneeVO updateOrderConsignee(OrderConsigneeVO orderConsignee) {

        OrderDetailVO orderDetailVO = this.orderQueryManager.getModel(orderConsignee.getOrderSn(), null);
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderDetailVO, orderDO);

        orderDO.setShipProvince(orderConsignee.getRegion().getProvince());
        orderDO.setShipProvinceId(orderConsignee.getRegion().getProvinceId());
        orderDO.setShipCity(orderConsignee.getRegion().getCity());
        orderDO.setShipCityId(orderConsignee.getRegion().getCityId());
        orderDO.setShipCounty(orderConsignee.getRegion().getCounty());
        orderDO.setShipCountyId(orderConsignee.getRegion().getCountyId());
        orderDO.setShipTown(orderConsignee.getRegion().getTown());
        orderDO.setShipTownId(orderConsignee.getRegion().getTownId());

        orderDO.setShipAddr(orderConsignee.getShipAddr());
        orderDO.setShipMobile(orderConsignee.getShipMobile());
        orderDO.setShipTel(orderConsignee.getShipTel());
        orderDO.setShipName(orderConsignee.getShipName());
        orderDO.setRemark(orderConsignee.getRemark());

        this.orderManager.update(orderDO);
        return orderConsignee;
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateOrderAddres(OrderConsigneeVO orderConsigneeVO) {
        // 修改的信息
        String orderSn = orderConsigneeVO.getOrderSn();
        String shippingType = orderConsigneeVO.getShippingType();
        String shipName = orderConsigneeVO.getShipName();
        String shipMobile = orderConsigneeVO.getShipMobile();
        Region region = orderConsigneeVO.getRegion();
        String shipAddr = orderConsigneeVO.getShipAddr();
        Integer leaderId = orderConsigneeVO.getLeaderId();
        LeaderDO leader =null;

        //  修改 es_order 值
        OrderDetailVO orderDetailVO = this.orderQueryManager.getModel(orderSn, null);

        //只有订单状态为待发货 或者 待付款 才能修改收货地址 -- PAID_OFF || CONFIRM
        String orderStatus = orderDetailVO.getOrderStatus();
        // if(!(orderStatus.equals(OrderStatusEnum.PAID_OFF.value()) || orderStatus.equals(OrderStatusEnum.FORMED.value()) || orderStatus.equals(OrderStatusEnum.CONFIRM.value()))){
        //     throw  new RuntimeException("只有订单状态为【待发货】或【待付款】才能修改收货地址!");
        // }

        String orderUpdateSql="UPDATE es_order SET ";
        List<Object> orderTerms=new ArrayList<>();

        String shetuanOrderUpdateSql="UPDATE es_shetuan_order SET ";
        List<Object> shetuanOrderTerms=new ArrayList<>();

        String wmsOrderUpdateSql="UPDATE wms_order SET ";
        List<Object> wmsOrderTerms=new ArrayList<>();

        // 收货信息改成派送
        if(shippingType.equals(ShipTypeEnum.GLOBAL.name())){
            orderUpdateSql=orderUpdateSql+"shipping_type='GLOBAL', " +
                    "ship_name=? ,ship_mobile=?,ship_province=?,ship_city=?,ship_county=?,ship_town=?,ship_addr=?," +
                    "leader_id=-1,pick_addr=null " +
                    "WHERE sn=?";
            orderTerms.add(shipName);
            orderTerms.add(shipMobile);
            orderTerms.add(region.getProvince());
            orderTerms.add(region.getCity());
            orderTerms.add(region.getCounty());
            orderTerms.add(region.getTown());
            orderTerms.add(shipAddr);
            orderTerms.add(orderDetailVO.getSn());

            shetuanOrderUpdateSql=shetuanOrderUpdateSql+
                    "leader_id=-1,leader_name=null,leader_mobile=null,leader_member_id=null,buyer_name=?,buyer_mobile=?," +
                    "pick_address=null,site_name=null,site_type=null " +
                    "WHERE order_sn=? ";
            shetuanOrderTerms.add(shipName);
            shetuanOrderTerms.add(shipMobile);
            shetuanOrderTerms.add(orderDetailVO.getSn());

            wmsOrderUpdateSql=wmsOrderUpdateSql+
                    "ship_name= ? ,ship_mobile= ?, ship_addr= ?, site_id= null, site_name= null, site_mobile= null, site_addr= null, shipping_type= 'GLOBAL' " +
                    "WHERE order_sn=? ";
            wmsOrderTerms.add(shipName);
            wmsOrderTerms.add(shipMobile);
            String address = (region.getProvince() + region.getCity() + region.getCounty() + region.getTown() + shipAddr).replaceAll("null", "");
            wmsOrderTerms.add(address);
            wmsOrderTerms.add(orderDetailVO.getSn());

            // 被修改的订单之前是自提订单 自提点佣金作废
            if(orderDetailVO.getShippingType().equals(ShipTypeEnum.SELF.name())){
                Double leaderMoney= 0.0;
                orderProfitManager.updateOrderProfit(leaderMoney,orderSn,"自提点",orderDetailVO.getLeader().getMemberId());
            }

        }else{
            leader = leaderManager.getById(leaderId);
            String address = (leader.getProvince() + leader.getCity() + leader.getCounty() + leader.getTown() + leader.getAddress()).replaceAll("null", "");

            orderUpdateSql=orderUpdateSql+"shipping_type='SELF', " +
                    "ship_name=? ,ship_mobile=?,ship_province=null,ship_city=null,ship_county=null,ship_town=null,ship_addr=null," +
                    "leader_id=?,pick_addr=? " +
                    "WHERE sn=?";
            orderTerms.add(shipName);
            orderTerms.add(shipMobile);
            orderTerms.add(leader.getLeaderId());
            orderTerms.add(address);
            orderTerms.add(orderDetailVO.getSn());

            shetuanOrderUpdateSql=shetuanOrderUpdateSql+
                    "leader_id=?,leader_name=?,leader_mobile=?,leader_member_id=?,buyer_name=?,buyer_mobile=?," +
                    "pick_address=?,site_name=?,site_type=? " +
                    "WHERE order_sn=? ";
            shetuanOrderTerms.add(leader.getLeaderId());
            shetuanOrderTerms.add(leader.getLeaderName());
            shetuanOrderTerms.add(leader.getLeaderMobile());
            shetuanOrderTerms.add(leader.getMemberId());
            shetuanOrderTerms.add(shipName);
            shetuanOrderTerms.add(shipMobile);
            shetuanOrderTerms.add(address);
            shetuanOrderTerms.add(leader.getSiteName());
            shetuanOrderTerms.add(leader.getSiteType());
            shetuanOrderTerms.add(orderDetailVO.getSn());

            wmsOrderUpdateSql=wmsOrderUpdateSql+
                    "ship_name= ? ,ship_mobile= ?, ship_addr= null, site_id= ?, site_name= ?, site_mobile= ?, site_addr= ?, shipping_type= 'SELF' " +
                    "WHERE order_sn=? ";
            wmsOrderTerms.add(shipName);
            wmsOrderTerms.add(shipMobile);
            wmsOrderTerms.add(leader.getLeaderId());
            wmsOrderTerms.add(leader.getLeaderName());
            wmsOrderTerms.add(leader.getLeaderMobile());
            wmsOrderTerms.add(address);
            wmsOrderTerms.add(orderDetailVO.getSn());

        }

        this.daoSupport.execute(orderUpdateSql,orderTerms.toArray());
        this.daoSupport.execute(shetuanOrderUpdateSql,shetuanOrderTerms.toArray());
        this.daoSupport.execute(wmsOrderUpdateSql,wmsOrderTerms.toArray());

        // 被修改的订单之前是配送的 改成自提 重新计算自提点佣金
        if(shippingType.equals(ShipTypeEnum.SELF.name()) && orderDetailVO.getShippingType().equals(ShipTypeEnum.GLOBAL.name())){
            Double leaderMoney = 0.0;
            Double selfRaisingRate = 0.0;
            String itemsJson = orderDetailVO.getItemsJson();
            JSONArray array = JSON.parseArray(itemsJson);
            for (int i = 0; i <array.size(); i++){
                JSONObject json = array.getJSONObject(i);
                String shetuanGoodsId = json.getString("shetuan_goods_id");
                ShetuanGoodsDO shetuanGoodsDO = shetuanGoodsManager.getById(Integer.parseInt(shetuanGoodsId));
                selfRaisingRate = shetuanGoodsDO.getSelfRaisingRate();
                // 计算自提点的佣金
                leaderMoney =CurrencyUtil.add(leaderMoney, CurrencyUtil.mul(orderDetailVO.getOrderPrice(),CurrencyUtil.div(selfRaisingRate, 100))) ;
            }

            orderProfitManager.updateOrderProfit(leaderMoney,orderSn,"自提点",leader.getMemberId());
        }
        // 更改配送系统订单状态
        // 判断订单是否是自提点订单
        String consigneeCustomerName = orderDetailVO.getShipName();
        String consigneeCustomerMobile = orderDetailVO.getShipMobile();
        String siteManName = null;
        MemberAddress memberAddress= new MemberAddress();
        if(leader != null){

            // 将订单信息改为自提点信息
            consigneeCustomerName = leader.getSiteName();
            consigneeCustomerMobile = leader.getLeaderMobile();
            siteManName = leader.getLeaderName();
            orderDetailVO.setShipProvince(leader.getProvince());
            orderDetailVO.setShipCity(leader.getCity());
            orderDetailVO.setShipCounty(leader.getCounty());
            orderDetailVO.setShipTown(leader.getTown());
            orderDetailVO.setShipAddr(leader.getAddress());

            memberAddress = new MemberAddress();
            // 经度
            memberAddress.setShipLng(leader.getLng());
            // 维度
            memberAddress.setShipLat(leader.getLat());
        }
        ShopDetailDO shopDetail = shopManager.getShopDetail(orderDetailVO.getSellerId());

        UpdateOrderDTO updateOrderDTO = new UpdateOrderDTO();
        updateOrderDTO.setUpsBillId(String.valueOf(orderDetailVO.getOrderId()));
        updateOrderDTO.setUpsBillNo(orderDetailVO.getSn());
        if(!EnvironmentUtils.isProd()){
            updateOrderDTO.setUpsBillNo("T" + updateOrderDTO.getUpsBillNo());
        }
        // 收货信息
        updateOrderDTO.setConsigneeCustomerName(consigneeCustomerName);
        updateOrderDTO.setConsigneeCustomerMobile(consigneeCustomerMobile);
        updateOrderDTO.setConsigneeName(orderDetailVO.getShipName());
        updateOrderDTO.setConsigneePhone(orderDetailVO.getShipMobile());
        updateOrderDTO.setConsigneeProvince(orderDetailVO.getShipProvince());
        updateOrderDTO.setConsigneeCity(orderDetailVO.getShipCity());
        updateOrderDTO.setConsigneeDistrict(orderDetailVO.getShipCounty());
        updateOrderDTO.setConsigneeStreet(orderDetailVO.getShipTown());
        updateOrderDTO.setConsigneeAddress(orderDetailVO.getShipAddr());

        if(memberAddress != null){
            updateOrderDTO.setConsigneeLng(memberAddress.getShipLng());
            updateOrderDTO.setConsigneeLat(memberAddress.getShipLat());
            updateOrderDTO.setDistance(this.countDistance(shopDetail, memberAddress));
        }

        updateOrderDTO.setIsSelfTake(shippingType.equals(ShipTypeEnum.SELF.name()) ? 1 : 0);
        updateOrderDTO.setSiteManName(siteManName);
        logger.debug("【修改配送订单请求报文】:"+JSON.toJSON(updateOrderDTO).toString());
        JsonBean jsonBean = deliveryManager.updateOrder(updateOrderDTO);;
        if(jsonBean.isSuccess()){
            logger.debug("修改配送订单下单成功");
        }else{
            logger.debug("修改配送订单下单失败" + JSON.toJSON(jsonBean).toString());
        }
    }
    private Double countDistance(ShopDetailDO shopDetail, MemberAddress memberAddress) {
        Double shopLng = shopDetail.getShopLng();
        Double shopLat = shopDetail.getShopLat();

        Double shipLng = memberAddress.getShipLng();
        Double shipLat = memberAddress.getShipLat();
        double distance = tencentManager.countDrivingDistance((shopLat + "," + shopLng), shipLat + "," + shipLng);
        return new BigDecimal(distance / 1000).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateOrderPrice(String orderSn, Double goodsPrice, Double shipPrice) {
        double orderPrice = goodsPrice + shipPrice;
        // 修改订单价格
        updateOrderPrice(orderSn, orderPrice);
        //修改运费价格
        String sql = "update es_order set shipping_price = ? where sn = ?";
        this.daoSupport.execute(sql, shipPrice, orderSn);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateOrderPrice(String orderSn, Double orderPrice) {

        //修改的订单价格不能小于等于0
        if (orderPrice <= 0) {
            throw new ServiceException(TradeErrorCode.E471.code(), "订单金额必须大于0");
        }

        //获取订单详情信息
        OrderDetailVO orderDetailVO = this.orderQueryManager.getModel(orderSn, null);

        //订单权限判断
        this.checkPermission(OrderPermission.seller, orderDetailVO);

        //获取交易单信息
        TradeDO tradeDO = tradeQueryManager.getModel(orderDetailVO.getTradeSn());

        //获取原订单金额
        Double oldPrice = orderDetailVO.getOrderPrice();
        //计算出原订单金额和修改后订单金额的差额
        Double differencePrice = CurrencyUtil.sub(oldPrice, orderPrice);
        //交易总价=原交易价格-差额
        Double tradePrice = CurrencyUtil.sub(tradeDO.getTotalPrice(), differencePrice);
        //优惠总额=原优惠金额-差额
        Double discountPrice = CurrencyUtil.add(tradeDO.getDiscountPrice(), differencePrice);

        //如果优惠总额小于0，那么将优惠总额设置为0
        if (discountPrice < 0) {
            discountPrice = 0.0;
        }

        //修改交易价格
        this.tradePriceManager.updatePrice(tradeDO.getTradeSn(), tradePrice, discountPrice);

        //获取修改订单中商品
        List<OrderSkuVO> list = JsonUtil.jsonToList(orderDetailVO.getItemsJson(), OrderSkuVO.class);

        //获取订单元数据信息
        Double fullMinus = Double.valueOf(this.orderMetaManager.getMetaValue(orderSn, OrderMetaKeyEnum.FULL_MINUS));
        Double cashBack = Double.valueOf(this.orderMetaManager.getMetaValue(orderSn, OrderMetaKeyEnum.CASH_BACK));
        Double couponPrice = Double.valueOf(this.orderMetaManager.getMetaValue(orderSn, OrderMetaKeyEnum.COUPON_PRICE));

        //订单商品原总价 = 订单实际支付价格 - 运费 + 返现金额 + 优惠券优惠金额
        Double goodsPrice = CurrencyUtil.add(CurrencyUtil.add(CurrencyUtil.sub(oldPrice, orderDetailVO.getShippingPrice()), cashBack), couponPrice);

        for (OrderSkuVO skuVO : list) {
            //商品原价
            Double originalPrice = skuVO.getOriginalPrice();
            //商品原价在订单商品总价中的占比（保留4位小数）
            Double ratio = CurrencyUtil.div(originalPrice, goodsPrice, 4);
            //商家修改订单价格后的商品价格 = 占比 * 订单价格
            Double nowPrice = CurrencyUtil.mul(ratio, orderPrice);
            //此商品实际支付总额
            Double actualTotol = CurrencyUtil.mul(nowPrice, skuVO.getNum());
            //重新初始化订单商品实际支付小计（退款时要用）
            skuVO.setActualPayTotal(actualTotol);
        }

        //订单修改价格后的优惠金额 = 订单原优惠金额 - 改价差额
        Double orderDiscoutPrice = CurrencyUtil.add(orderDetailVO.getDiscountPrice(), differencePrice);
        //如果订单修改价格后的优惠金额小于优惠券优惠的金额，那么将订单修改价格后的优惠金额设置为优惠券优惠的金额，防止当修改后的订单总价高于原价时返现金额和商品总价为负数
        if (orderDiscoutPrice < couponPrice) {
            orderDiscoutPrice = couponPrice;
        }

        //修改订单价格
        String sql = "update es_order set order_price = ?,need_pay_money = ?,discount_price = ?,items_json = ? where sn = ?";
        this.daoSupport.execute(sql, orderPrice, orderPrice, orderDiscoutPrice, JsonUtil.objectToJson(list), orderSn);

        //如果此订单已经开发票需要修改发票订单金额
        if (orderDetailVO.getNeedReceipt().equals(1)) {
            sql = "update es_receipt_history set order_price = ? where order_sn = ?";
            this.memberDaoSupport.execute(sql, orderPrice, orderSn);
        }

        //修改订单元数据信息，此处是为了退款时的金额计算正确所做的操作
        this.orderMetaManager.updateMetaValue(orderSn, OrderMetaKeyEnum.FULL_MINUS, CurrencyUtil.add(fullMinus, differencePrice).toString());
        this.orderMetaManager.updateMetaValue(orderSn, OrderMetaKeyEnum.CASH_BACK, CurrencyUtil.add(cashBack, differencePrice).toString());

        //记录操作日志
        OrderLogDO orderLogDO = new OrderLogDO();
        orderLogDO.setMessage("商家修改订单价格");
        orderLogDO.setOrderSn(orderSn);
        //目前此方法只有商家会调用，所以可以直接读当前登录的商家
        orderLogDO.setOpName(UserContext.getSeller().getSellerName());
        orderLogDO.setOpTime(DateUtil.getDateline());
        this.orderLogManager.add(orderLogDO);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateCommentStatus(String orderSn, CommentStatusEnum commentStatus) {
        String sql = "update es_order set comment_status = ? where sn = ? ";
        this.daoSupport.execute(sql, commentStatus.name(), orderSn);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateItemJson(String itemsJson, String orderSn) {
        String sql = "update es_order set items_json = ? where  sn = ? ";
        this.daoSupport.execute(sql, itemsJson, orderSn);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateOrderStatus(String orderSn, OrderStatusEnum orderStatus) {

        StringBuffer sqlBuffer = new StringBuffer("update es_order set order_status = ? ");

        if (OrderStatusEnum.PAID_OFF.equals(orderStatus)) {
            sqlBuffer.append(",pay_status = '" + PayStatusEnum.PAY_YES.value() + "'");
        }

        sqlBuffer.append(" where sn = ? ");

        this.daoSupport.execute(sqlBuffer.toString(), orderStatus.value(), orderSn);
    }


    @Override
    public void executeOperate(String orderSn, OrderPermission permission, OrderOperateEnum orderOperate, Object paramVO) {
        OrderStatusChangeMsg message = orderOperateManager.updateOrder(orderSn, permission, orderOperate, paramVO);

        // 返回的消息不为空（组合支付时，微信支付成功回调，但是钱包未支付时会返回NULL），发送订单状态变化消息
        if (!ObjectUtils.isEmpty(message)) {
            this.amqpTemplate.convertAndSend(AmqpExchange.ORDER_STATUS_CHANGE, "order-change-queue", message);
        }
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public OrderStatusChangeMsg updateOrder(String orderSn, OrderPermission permission, OrderOperateEnum orderOperate, Object paramVO) {
        // 获取此订单
        OrderDetailVO orderDetailVO = orderQueryManager.getModel(orderSn, null);

        //1、验证操作者的权限
        this.checkPermission(permission, orderDetailVO);

        //2、验证此订单可进行的操作
        this.checkAllowable(permission, orderDetailVO, orderOperate);

        long nowTime = DateUtil.getDateline();

        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderDetailVO, orderDO);

        //要变更的订单状态
        OrderStatusEnum newStatus = null;

        //日志信息
        String logMessage = "操作信息";

        String operator = "系统默认";

        switch (orderOperate) {
            case CONFIRM:

                ConfirmVO confirmVO = (ConfirmVO) paramVO;
                logMessage = "确认订单";
                newStatus = OrderStatusEnum.CONFIRM;
                this.daoSupport.execute("update es_order set order_status=?  where sn=? ", OrderStatusEnum.CONFIRM.value(),
                        confirmVO.getOrderSn());
                orderDO.setOrderStatus(OrderStatusEnum.CONFIRM.name());
                break;

            case PAY:
                // 幂等
                if (orderDetailVO.getOrderStatus().equals(OrderStatusEnum.PAID_OFF.value())) {
                    logger.info("订单已支付，幂等处理："+orderDetailVO.getSn()+orderDetailVO.toString());
                    return null;
                }

                Double alreadyPayPrice = 0.0d;
                String paymentPluginId = orderDO.getPaymentPluginId();
                PayParam payParam = (PayParam) paramVO;
                if (PaymentPluginEnum.mergePayPlugin.toString().equals(paymentPluginId)) {
                    //校验是否有交易未完成
                    //从redis中获取支付单主键
                    String billIds = stringRedisTemplate.opsForValue().get(orderDO.getSn() + "_" + paymentPluginId);
                    List<PaymentBillDO> PaymentBillList = paymentBillManager.getBillByBillIds(billIds);
                    for (PaymentBillDO paymentBill : PaymentBillList) {
                        if (paymentBill.getIsPay() != 1 && PaymentPluginEnum.walletPayPlugin.toString().equals(paymentBill.getPaymentPluginId())) {
                            //此订单有未完成的钱包支付金额，发送钱包支付消息
                            this.amqpTemplate.convertAndSend(AmqpExchange.WALLET_PAY_INVOKE, AmqpExchange.WALLET_PAY_INVOKE + "_QUEUE", paymentBill);
                            return null;
                        }
                        //组合支付的话，已支付金额为所有已支付的支付单的支付金额累加
                        alreadyPayPrice = CurrencyUtil.add(alreadyPayPrice, paymentBill.getTradePrice());
                    }
                } else {
                    //不是组合支付，已支付金额为本次支付的金额
                    alreadyPayPrice = payParam.getPayPrice();
                }
                logMessage = "支付订单";
                newStatus = OrderStatusEnum.PAID_OFF;
                String returnTradeNo = payParam.getReturnTradeNo();

                switch (permission) {
                    case buyer:
                        operator = orderDetailVO.getMemberName();
                        break;

                    case client:
                        operator = orderDetailVO.getMemberName();
                        break;

                    case seller:
                        operator = orderDetailVO.getSellerName();
                        break;

                    case admin:
                        operator = AdminUserContext.getAdmin().getUsername();
                        // 后台点击确认收款，清空支付方式
                        this.daoSupport.execute("update es_order set payment_plugin_id = null,payment_method_name=null where sn=?", orderDO.getSn());
                        break;
                    default:
                        break;
                }

                //款到发货订单 卖家不能确认收款
                if (permission.equals(OrderPermission.seller) && orderDO.getPaymentType().equals(PaymentTypeEnum.ONLINE.name())) {
                    throw new NoPermissionException("无权操作此订单");
                }
                // 付款金额和订单金额不相等
                if (alreadyPayPrice.compareTo(orderDO.getNeedPayMoney()) != 0) {
                    throw new ServiceException(TradeErrorCode.E454.code(), "付款金额和应付金额不一致");
                }

                // 如果是虚拟商品，生成核销码保存到数据库（核销码规则：四位随机数+orderId+当前时间戳后四位）
                if (orderDO.getExpiryDay() != null) {
                    String endCode = ("" + nowTime).substring(6);
                    String verificationCode = "" + Math.round(Math.random() * 9) + Math.round(Math.random() * 9) + Math.round(Math.random() * 9) + Math.round(Math.random() * 9);
                    verificationCode += orderDO.getOrderId() + endCode;
                    orderDO.setVerificationCode(verificationCode);
                }

                // 公共参数
                orderDO.setOrderStatus(OrderStatusEnum.PAID_OFF.value());
                orderDO.setPayStatus(PayStatusEnum.PAY_YES.value());
                orderDO.setPayMoney(alreadyPayPrice);
                orderDO.setPaymentTime(nowTime);
                //判断支付方式
                if (orderDO.getPaymentType().equals(PaymentTypeEnum.COD.value())) {
                    orderDO.setShipStatus(ShipStatusEnum.SHIP_ROG.value());
                } else {
                    orderDO.setShipStatus(ShipStatusEnum.SHIP_NO.value());
                    orderDO.setPayOrderNo(returnTradeNo);
                }

                // 更新订单表和交易表
                orderManager.update(orderDO);
                this.daoSupport.execute("update es_trade set trade_status=? where trade_sn=?",
                        TradeStatusEnum.PAID_OFF.value(), orderDO.getTradeSn());

                this.updateShetuanGoodsBuyNum(orderDO);
                break;
            case SPLIT_SHIP:

                // 1.检测订单是否已经申请售后
                if (ServiceStatusEnum.APPLY.name().equals(orderDO.getServiceStatus())
                        || ServiceStatusEnum.PASS.name().equals(orderDO.getServiceStatus())) {
                    throw new ServiceException(TradeErrorCode.E451.code(), "订单已申请退款，不能发货");
                }
                DeliveryVO splitDelivery = (DeliveryVO) paramVO;

                // 2.获取未发货的商品ID
                List<Integer> skuIds = splitDelivery.getSkuIds();
                List<OrderItemsDO> orderItems = orderQueryManager.getOrderItems(splitDelivery.getOrderSn());
                List<Integer> orderItemSkuIds = new ArrayList<>();
                List<OrderSkuVO> skuList = JsonUtil.jsonToList(orderDO.getItemsJson(),OrderSkuVO.class);
                if (CollectionUtils.isEmpty(skuList)) {
                    throw new ServiceException(AftersaleErrorCode.E602.name(), AftersaleErrorCode.E602.describe());
                }
                for (OrderItemsDO orderItem : orderItems) {
                    String shipStatus = orderItem.getShipStatus();
                    for (OrderSkuVO orderSkuVO : skuList) {
                        // 只有订单中商品售后状态为NOT_APPLY，且商品未发货才算是未发货的商品（已申请售后的商品不算未发货）
                        if (orderItem.getProductId().equals(orderSkuVO.getSkuId()) && ServiceStatusEnum.NOT_APPLY.value().equals(orderSkuVO.getServiceStatus())) {
                            if ((ObjectUtils.isEmpty(shipStatus)) || ShipStatusEnum.SHIP_NO.value().equals(shipStatus)) {
                                orderItemSkuIds.add(orderItem.getProductId());
                            }
                        }
                    }
                }

                // 3.排序比较要发货的skuIDs 和 未发货的skuIds
                Collections.sort(skuIds);
                Collections.sort(orderItemSkuIds);
                // 完全相同，相当于订单全部发货
                if (skuIds.equals(orderItemSkuIds)) {
                    logMessage = "订单发货";
                    newStatus = OrderStatusEnum.SHIPPED;
                    orderDO.setOrderStatus(newStatus.value());
                } else if (orderItemSkuIds.containsAll(skuIds)) {
                    // 未发货的skuIds 包含 要发货的skuIDs，订单分批发货
                    logMessage = "订单分批发货";
                    // PART_SHIPPED 部分发货状态只用于内部发生消息使用，部分发布的订单状态还是PAID_OFF，全部发货变为SHIPPED
                    newStatus = OrderStatusEnum.PART_SHIPPED;
                    orderDO.setOrderStatus(OrderStatusEnum.PAID_OFF.value());
                } else {
                    throw new ServiceException(TradeErrorCode.E451.code(), "发货失败，商品异常");
                }

                operator = splitDelivery.getOperator();
                // 4.修改order的发货信息和订单售后状态
                orderDO.setShipStatus(ShipStatusEnum.SHIP_YES.value());
                orderDO.setShipNo(splitDelivery.getDeliveryNo());
                orderDO.setShipTime(nowTime);
                orderDO.setLogiId(splitDelivery.getLogiId());
                orderDO.setLogiName(splitDelivery.getLogiName());

                // 5.更新订单和订单详情的订单状态和发货信息
                this.daoSupport.update(orderDO, orderDO.getOrderId());
                String skuIdsString = StringUtil.implode(",", skuIds.toArray());
                this.daoSupport.execute("update es_order_items set ship_status=?, ship_no=? ,ship_time = ?, logi_id=?, logi_name=? " +
                                " where order_sn=? and product_id in(" + skuIdsString + ")",
                        ShipStatusEnum.SHIP_YES.value(), splitDelivery.getDeliveryNo(), nowTime,
                        splitDelivery.getLogiId(), splitDelivery.getLogiName(), orderDO.getSn());
                // 6.redis中存入本次发货的商品IDs
                String key = OrderOperateEnum.SPLIT_SHIP.name() + "_" + orderDO.getSn();
                stringRedisTemplate.opsForValue().set(key, skuIdsString, 300, TimeUnit.SECONDS);

                break;
            case SHIP:

                //检测订单是否已经申请售后
                if (ServiceStatusEnum.APPLY.name().equals(orderDO.getServiceStatus())
                        || ServiceStatusEnum.PASS.name().equals(orderDO.getServiceStatus())) {
                    throw new ServiceException(TradeErrorCode.E455.code(), "订单已申请退款，不能发货");
                }
                DeliveryVO deliveryVO = (DeliveryVO) paramVO;

                //检测订单发货方式是否修改
                // if ( deliveryVO.getShipType()!= null && !deliveryVO.getShipType().equals(orderDetailVO.getShippingType())) {
                    // this.daoSupport.execute("update es_order set shipping_type=?  where sn=? ", deliveryVO.getShipType(),  orderDetailVO.getSn());
                // }

                logMessage = "订单发货";
                newStatus = OrderStatusEnum.SHIPPED;
                operator = deliveryVO.getOperator();

                this.daoSupport.execute("update es_order set order_status=? ,ship_status=?,service_status=?,ship_no=? ,ship_time = ?,logi_id=?,logi_name=? where sn=? ",
                        OrderStatusEnum.SHIPPED.value(), ShipStatusEnum.SHIP_YES.value(), ServiceStatusEnum.NOT_APPLY.value(), deliveryVO.getDeliveryNo(), nowTime,
                        deliveryVO.getLogiId(), deliveryVO.getLogiName(), orderDetailVO.getSn());
                this.daoSupport.execute("update es_order_items set ship_status=?, ship_no=? ,ship_time = ?, logi_id=?, logi_name=? where order_sn=? ",
                        ShipStatusEnum.SHIP_YES.value(), deliveryVO.getDeliveryNo(), nowTime,
                        deliveryVO.getLogiId(), deliveryVO.getLogiName(), orderDO.getSn());

                orderDO.setOrderStatus(OrderStatusEnum.SHIPPED.name());
                orderDO.setShipStatus(ShipStatusEnum.SHIP_YES.value());
                orderDO.setServiceStatus(ServiceStatusEnum.NOT_APPLY.value());
                orderDO.setShipNo(deliveryVO.getDeliveryNo());
                orderDO.setShipTime(nowTime);
                orderDO.setLogiId(deliveryVO.getLogiId());
                orderDO.setLogiName(deliveryVO.getLogiName());

                break;
            case ROG:

                RogVO rogVO = (RogVO) paramVO;
                logMessage = "确认收货";
                newStatus = OrderStatusEnum.ROG;
                operator = rogVO.getOperator();

                //订单售后状态  by JFENG 订单收货之后仍然可以进行售后申请
                //String orderServiceStatus = ServiceStatusEnum.EXPIRED.value();

                this.daoSupport.execute("update es_order set order_status=? ,ship_status=?,signing_time = ?  where sn=? ",
                        OrderStatusEnum.ROG.value(), ShipStatusEnum.SHIP_ROG.value(), nowTime, orderDO.getSn());

                orderDO.setOrderStatus(OrderStatusEnum.ROG.name());
                orderDO.setShipStatus(ShipStatusEnum.SHIP_ROG.value());
                orderDO.setSigningTime(nowTime);

                break;

            case CANCEL:

                CancelVO cancelVO = (CancelVO) paramVO;
                logMessage = "取消订单";
                newStatus = OrderStatusEnum.CANCELLED;
                operator = cancelVO.getOperator();

                this.daoSupport.execute("update es_order set order_status=? , cancel_reason=? where sn=? ",
                        OrderStatusEnum.CANCELLED.value(), cancelVO.getReason(), orderDO.getSn());
                orderDO.setOrderStatus(OrderStatusEnum.CANCELLED.name());
                orderDO.setCancelReason(cancelVO.getReason());
                // 如果有钱包支付金额且是用户主动取消订单（已确认的订单）要把冻结用户的金额解冻
                if (orderDO.getWalletPayPrice() > 0 && permission.equals(OrderPermission.buyer)) {
                    this.unfreezeCancelPay(orderDO);
                }
                break;

            case COMPLETE:

                CompleteVO completeVO = (CompleteVO) paramVO;
                logMessage = "订单已完成";
                newStatus = OrderStatusEnum.COMPLETE;
                operator = completeVO.getOperator();

                this.daoSupport.execute("update es_order set order_status=?,complete_time=?  where sn=? ", OrderStatusEnum.COMPLETE.value(),
                        nowTime, orderSn);
                orderDO.setOrderStatus(OrderStatusEnum.COMPLETE.name());
                orderDO.setCompleteTime(nowTime);

                break;

            default:
                break;
        }


        // 记录日志
        OrderLogDO orderLogDO = new OrderLogDO();
        orderLogDO.setMessage(logMessage);
        orderLogDO.setOrderSn(orderSn);
        orderLogDO.setOpName(operator);
        orderLogDO.setOpTime(DateUtil.getDateline());
        this.orderLogManager.add(orderLogDO);

        //异步消息
        OrderStatusChangeMsg message = new OrderStatusChangeMsg();
        message.setOrderDO(orderDO);
        message.setOldStatus(OrderStatusEnum.valueOf(orderDetailVO.getOrderStatus()));
        message.setNewStatus(newStatus);
        return message;
    }

    private void updateShetuanGoodsBuyNum(OrderDO orderDO) {
        if(orderDO.getOrderType().equals(OrderTypeEnum.shetuan.name())){
            List<OrderSkuVO> skuVOList = JsonUtil.jsonToList(orderDO.getItemsJson(), OrderSkuVO.class);
            // 更新REDIS 中社区团购商品购买数量 【每天晚上定时清理失效key】
            for (OrderSkuVO orderSkuVO : skuVOList) {
                Integer shetuanGoodsId = orderSkuVO.getShetuanGoodsId();
                if(shetuanGoodsId!=null){
                    redisTemplate.opsForValue().increment(CachePrefix.ST_BUY_NUM.getPrefix()+shetuanGoodsId,orderSkuVO.getNum());
                }
            }
        }
    }

    @Override
    public void unfreezeCancelPay(OrderDO order){
        String sn = order.getSn();
        PaymentBillDO paymentBill = paymentBillManager.getBillBySnAndTradeTypeAndPaymentPluginID(sn,
                TradeType.order.toString(), PaymentPluginEnum.walletPayPlugin.toString());
        if (paymentBill == null) {
            throw new RuntimeException("解冻金额异常【" + sn + "】：获取支付单为空");
        }
        Member member = memberManager.getModel(order.getMemberId());
        if (member == null) {
            throw new RuntimeException("解冻金额异常【" + sn + "】：获取用户为空");
        }
        UnfreezeCancelPayReqDTO unfreezeCancelPayReqDTO = this.buildUnfreezeCancelPayReqDTO(paymentBill, member);
        UnfreezeCancelPayRespDTO unfreezeCancelPayRespDTO = accountPaymentManager.unfreezeCancelPay(unfreezeCancelPayReqDTO);
        if (!unfreezeCancelPayRespDTO.isSuccess()) {
            throw new RuntimeException("解冻金额异常：【sn=" + sn + "】，【out_trade_no=" + unfreezeCancelPayReqDTO.getTradeVoucherNo()
                    + "】，" + unfreezeCancelPayRespDTO.getMessage());
        }
    }

    //初始化一个取消冻结支付请求DTO
    private UnfreezeCancelPayReqDTO buildUnfreezeCancelPayReqDTO(PaymentBillDO paymentBill, Member member){

        UnfreezeCancelPayReqDTO unfreezeCancelPayReqDTO = new UnfreezeCancelPayReqDTO();
        unfreezeCancelPayReqDTO.setAccountType(AccountTypeEnum.MEMBER_MASTER.getIndex());
        unfreezeCancelPayReqDTO.setAmount(BigDecimal.valueOf(paymentBill.getTradePrice()));
        unfreezeCancelPayReqDTO.setDraweeMemberId(member.getAccountMemberId());
        unfreezeCancelPayReqDTO.setOperatorId(member.getMemberId().toString());
        unfreezeCancelPayReqDTO.setOperatorName(member.getNickname());
        unfreezeCancelPayReqDTO.setTradeVoucherNo(paymentBill.getOutTradeNo());
        return unfreezeCancelPayReqDTO;
    }

    @Override
    public void updateTradeStatus(String sn, OrderStatusEnum orderStatus) {
        String sql = "update es_trade set trade_status = ? where trade_sn = ?";
        this.daoSupport.execute(sql, orderStatus.value(), sn);
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void updateItemRefundPrice(OrderDetailVO order) {
        //获取订单的满减优惠总额
        double fullMinus = order.getFullMinus();
        //获取订单优惠券优惠的总额
        double couponPrice = order.getCouponPrice();
        //获取订单商品集合
        List<OrderSkuVO> skuVOList = order.getOrderSkuList();

        //订单参与满减促销活动商品的总额（还未减去满减优惠的金额总计）
        double fmTotal = 0.00;
        //订单所有商品的总额（还未减去满减优惠和优惠券优惠的金额总计）
        double allTotal = 0.00;
        //订单参与满减促销活动的商品集合
        List<OrderSkuVO> fmList = new ArrayList<>();
        //订单未参与满减促销活动的商品集合
        List<OrderSkuVO> noFmList = new ArrayList<>();

        //循环获取订单中参与满减活动的商品集合、为参与满减活动的商品集合、参与满减活动商品金额总计和订单商品全部金额总计
        for (OrderSkuVO orderSkuVO : skuVOList) {
            //如果orderSkuVo中组合活动集合不为空并且长度不为0，那么则证明当前商品参与了满减活动
            if (orderSkuVO.getGroupList() != null && orderSkuVO.getGroupList().size() != 0) {
                fmTotal = CurrencyUtil.add(fmTotal, orderSkuVO.getSubtotal());
                fmList.add(orderSkuVO);
            } else {
                noFmList.add(orderSkuVO);
            }

            allTotal = CurrencyUtil.add(allTotal, orderSkuVO.getSubtotal());
        }

        //获取订单计算退款金额方式
        int countType = this.getCountType(fmList, noFmList, couponPrice);

        /**
         * 计算订单项退款金额
         *
         * countType说明:
         * 1：订单所有商品都没有参与满减优惠活动也没有使用优惠券
         * 2：订单所有商品都没有参与满减活动，但是订单使用了优惠券
         * 3：订单商品全部都参与了满减活动，但是没有使用优惠券
         * 4：订单商品一部分参与了满减活动，一部分没有参与满减活动，并且没有使用优惠券
         * 5：订单商品全部都参与了满减活动并且使用了优惠券
         * 6：订单商品一部分参与了满减活动，一部分没有参与满减活动，并且使用了优惠券
         */
        switch (countType) {
            case 1:
                updateItemRefundPrice(order.getSn(), skuVOList);
                break;

            case 2:
                //获取未参与满减活动商品总数-1的数值（为了兼容金额比例无法整除而导致多个商品的退款金额总和与退款金额不一致的问题）
                int noFmNum = noFmList.size() - 1;

                //剩余的订单优惠券优惠总额
                double surplusCouponPrice = couponPrice;

                updateCouponItemRefundPrice(order.getSn(), noFmList, couponPrice, allTotal, noFmNum, surplusCouponPrice);
                break;

            case 3:
                updateFmItemsRefundPrice(order.getSn(), fmList, fullMinus, fmTotal);
                break;

            case 4:
                updateFmItemsRefundPrice(order.getSn(), fmList, fullMinus, fmTotal);

                updateItemRefundPrice(order.getSn(), noFmList);
                break;

            case 5:
                updateFmItemsRefundPrice(order.getSn(), fmList, CurrencyUtil.add(fullMinus, couponPrice), fmTotal);
                break;

            case 6:
                updateFmCouponItemsRefundPrice(order.getSn(), fmList, noFmList, couponPrice, allTotal, fullMinus, fmTotal);
                break;

            default:
                break;
        }
    }

    @Override
    public void updateOrderServiceStatus(String orderSn, String statusEnum) {

        ServiceStatusEnum serviceStatusEnum = ServiceStatusEnum.valueOf(statusEnum);
        this.updateServiceStatus(orderSn, serviceStatusEnum);

        OrderDetailVO orderDetailVO = this.orderQueryManager.getModel(orderSn, null);
        List<OrderSkuVO> orderSkuVOList = orderDetailVO.getOrderSkuList();
        for (OrderSkuVO orderSkuVO : orderSkuVOList) {
            orderSkuVO.setServiceStatus(serviceStatusEnum.name());
        }
        this.updateItemJson(JsonUtil.objectToJson(orderSkuVOList), orderSn);

    }

    @Override
    public void updateOrderItemServiceStatus(String sn, List<OrderSkuDTO> orderSkuDTOList) {
        OrderDetailVO orderDetailVO = this.orderQueryManager.getModel(sn, null);
        List<OrderSkuVO> orderSkuVOList = orderDetailVO.getOrderSkuList();
        int applySkuNum=0;
        for (OrderSkuVO orderSkuVO : orderSkuVOList) {
            for (OrderSkuDTO orderSkuDTO : orderSkuDTOList) {
                if (orderSkuVO.getSkuId().equals(orderSkuDTO.getSkuId())) {
                    orderSkuVO.setServiceStatus(orderSkuDTO.getServiceStatus());
                }
            }
            if(orderSkuVO.getServiceStatus().equals(ServiceStatusEnum.APPLY.value())){
                applySkuNum+=1;
            }
        }
        this.updateItemJson(JsonUtil.objectToJson(orderSkuVOList), sn);
        // 单个商品申请退款意味着整单取消了
        if(applySkuNum==orderSkuVOList.size()){
            String sql = "update es_order set service_status = ? where sn = ?";
            this.daoSupport.execute(sql, ServiceStatusEnum.APPLY.value(), sn);
        }
    }

    @Override
    public void editOrderShopName(Integer shopId, String shopName) {
        String sql = "update es_order set seller_name = ? where seller_id = ?";
        this.daoSupport.execute(sql, shopName, shopId);
    }

    @Override
    public void updateItemsCommentStatus(String orderSn, Integer goodsId, CommentStatusEnum commentStatus) {
        String sql = "update es_order_items set comment_status = ? where order_sn = ? and goods_id = ? ";
        this.daoSupport.execute(sql, commentStatus.name(), orderSn, goodsId);
        checkOrderCommentStatus(orderSn, commentStatus);
    }

    @Override
    public void deleteOrder(String orderSn) {
        String sql = "select * from es_order  where sn = ?";
        OrderDO orderDO = this.daoSupport.queryForObject(sql, OrderDO.class, orderSn);
        if(orderDO==null || !orderDO.getOrderStatus().equals(OrderStatusEnum.CANCELLED.value())){
            throw new ServiceException("500","只能删除已取消的订单");
        }
        this.daoSupport.delete(OrderDO.class,orderDO.getOrderId());
    }

    @Override
    public Map<String, String> virtualOrderShip(String verificationCode) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "false");
        map.put("desc", null);

        if (StringUtil.isEmpty(verificationCode) || verificationCode.length() <= 8) {
            map.put("desc", "核销码格式错误");
            return map;
        }

        // 核销码的组成： 前四位随机数   + 订单id + 时间戳后四位
        String orderIdString = verificationCode.substring(4, verificationCode.length() - 4);
        if (StringUtil.isEmpty(orderIdString)) {
            map.put("desc", "核销码格式错误");
            return map;
        }

        // 1、获取订单ID
        int orderId = Integer.parseInt(orderIdString);
        OrderDO orderDO = this.daoSupport.queryForObject(OrderDO.class, orderId);

        if (orderDO == null) {
            map.put("desc", "未查询到订单");
            return map;
        }

        Seller seller = UserContext.getSeller();
        if (seller == null || !seller.getSellerId().equals(orderDO.getSellerId())) {
            map.put("desc", "当前订单与店铺不匹配，不能核销");
            return map;
        }

        Long createTime = orderDO.getCreateTime();
        Integer expiryDay = orderDO.getExpiryDay();

        if (createTime == null || expiryDay == null) {
            map.put("desc", "当前订单不能核销");
            return map;
        }

        // 不需要限制过期时间
        // if ((createTime + expiryDay * 24 * 60 * 60) < DateUtil.getDateline()) {
        //     map.put("desc", "核销码已过期");
        //     return map;
        // }

        if (!OrderStatusEnum.PAID_OFF.value().equals(orderDO.getOrderStatus())) {
            map.put("desc", "订单状态不能消费");
            return map;
        }

        if (!verificationCode.equals(orderDO.getVerificationCode())) {
            map.put("desc", "核销码不匹配");
            return map;
        }

        // 3、改变订单状态,记录使用日期
        orderDO.setOrderStatus(OrderStatusEnum.ROG.value());
        orderDO.setVerificationTime(DateUtil.getDateline());
        this.daoSupport.update(orderDO, orderId);

        map.put("status", "true");
        map.put("desc", "核销成功");
        return map;
    }

    private void checkOrderCommentStatus(String orderSn, CommentStatusEnum commentStatusEnum) {

        List<Object> term = new ArrayList<>();
        term.add(orderSn);
        //如果要修改为待追评状态，则需要检测订单项中是否包含未评论的
        if (CommentStatusEnum.WAIT_CHASE.equals(commentStatusEnum)) {
            term.add(CommentStatusEnum.UNFINISHED.name());
        }

        //如果要修改为评论完成状态，则需要检测订单项中是否包含待追评的信息
        if (CommentStatusEnum.FINISHED.equals(commentStatusEnum)) {
            term.add(CommentStatusEnum.WAIT_CHASE.name());
        }

        int count = this.daoSupport.queryForInt("select count(1) from es_order_items where order_sn = ? and comment_status = ? ", term.toArray());
        //如果不存在了，则修改订单的评论状态，否则不修改
        if (count == 0) {
            this.updateCommentStatus(orderSn, commentStatusEnum);
        }
    }

    /**
     * 修改订单项的退款金额
     * 调用说明：1.针对订单全部商品都没有参与满减活动也没有使用优惠券的情况
     * 2.针对订单部分商品都没有参与满减活动也没有使用优惠券的情况
     *
     * @param orderSn   订单编号
     * @param skuVOList 订单商品数据
     */
    private void updateItemRefundPrice(String orderSn, List<OrderSkuVO> skuVOList) {
        for (OrderSkuVO orderSkuVO : skuVOList) {
            double refundPrice = orderSkuVO.getActualPayTotal();
            updateRefundPrice(orderSn, orderSkuVO, refundPrice);
        }
    }

    /**
     * 修改订单项的退款金额
     * 调用说明：1.针对订单全部商品都参与了满减促销活动并且没有使用优惠券的情况
     * 2.针对订单全部商品都参与了满减促销活动并且使用了优惠券的情况
     * 3.针对订单一部分商品参与了满减促销活动并且没有使用优惠券的情况
     *
     * @param orderSn   订单编号
     * @param fmList    参与满减促销活动的订单商品集合
     * @param fullMinus 满减的总金额
     * @param fmTotal   参与满减促销活动的商品金额总计（还未减去满减优惠的金额总计）
     */
    private void updateFmItemsRefundPrice(String orderSn, List<OrderSkuVO> fmList, double fullMinus, double fmTotal) {
        //获取参与满减活动商品总数-1的数值（为了兼容金额比例无法整除而导致多个商品的退款金额总和与退款金额不一致的问题）
        int num = fmList.size() - 1;
        //剩余的订单满减总额
        double surplusFmPrice = fullMinus;

        for (int i = 0; i < fmList.size(); i++) {
            OrderSkuVO orderSkuVO = fmList.get(i);
            //当前商品的应退款金额
            double refundPrice = 0.00;

            if (i != num) {
                //获取当前商品满减的占比金额
                double fmRatioPrice = CurrencyUtil.mul(CurrencyUtil.div(orderSkuVO.getSubtotal(), fmTotal, 4), fullMinus);
                //当前商品应退款金额=金额总计-满减的占比金额
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), fmRatioPrice);
                //计算剩余的满减总额
                surplusFmPrice = CurrencyUtil.sub(surplusFmPrice, fmRatioPrice);
            } else {
                //当前商品应退款金额=金额总计-剩余的满减总额
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), surplusFmPrice);
            }

            updateRefundPrice(orderSn, orderSkuVO, refundPrice);
        }
    }

    /**
     * 修改订单项的退款金额
     * 调用说明：针对订单一部分商品参与了满减促销活动，一部分商品没有参与满减促销活动并且还使用了优惠券的情况
     *
     * @param orderSn     订单编号
     * @param fmList      参与满减促销活动的订单商品集合
     * @param noFmList    未参与满减促销活动的订单商品集合
     * @param couponPrice 订单使用的优惠券优惠金额
     * @param allTotal    所有商品的金额总计（还未减去满减优惠和优惠券优惠的金额总计）
     * @param fullMinus   满减的总金额
     * @param fmTotal     参与满减促销活动的商品金额总计（还未减去满减优惠的金额总计）
     */
    private void updateFmCouponItemsRefundPrice(String orderSn, List<OrderSkuVO> fmList, List<OrderSkuVO> noFmList, double couponPrice, double allTotal, double fullMinus, double fmTotal) {
        //获取参与满减活动商品总数-1的数值（为了兼容金额比例无法整除而导致多个商品的退款金额总和与退款金额不一致的问题）
        int fmMum = fmList.size() - 1;
        //获取未参与满减活动商品总数-1的数值（为了兼容金额比例无法整除而导致多个商品的退款金额总和与退款金额不一致的问题）
        int noFmNum = noFmList.size() - 1;

        //剩余的订单满减总额
        double surplusFmPrice = fullMinus;
        //剩余的订单优惠券优惠总额
        double surplusCouponPrice = couponPrice;

        for (int i = 0; i < fmList.size(); i++) {
            OrderSkuVO orderSkuVO = fmList.get(i);
            double refundPrice = 0.00;

            if (i != fmMum) {
                //获取当前商品满减的占比金额
                double fmRatioPrice = CurrencyUtil.mul(CurrencyUtil.div(orderSkuVO.getSubtotal(), fmTotal, 4), fullMinus);
                //获取当前商品优惠券优惠的占比金额
                double couponRatioPrice = CurrencyUtil.mul(CurrencyUtil.div(orderSkuVO.getSubtotal(), allTotal, 4), couponPrice);
                //当前商品应退款金额=金额总计-(满减的占比金额+优惠券优惠的占比金额)
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), CurrencyUtil.add(fmRatioPrice, couponRatioPrice));
                //计算剩余的满减总额
                surplusFmPrice = CurrencyUtil.sub(surplusFmPrice, fmRatioPrice);
                //计算剩余的优惠券优惠总额
                surplusCouponPrice = CurrencyUtil.sub(surplusCouponPrice, couponRatioPrice);
            } else {
                //获取当前商品优惠券优惠的占比金额
                double couponRatioPrice = CurrencyUtil.mul(CurrencyUtil.div(orderSkuVO.getSubtotal(), allTotal, 4), couponPrice);
                //当前商品应退款金额=金额总计-(优惠券优惠的占比金额+剩余的满减总额)
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), CurrencyUtil.add(couponRatioPrice, surplusFmPrice));
                //计算剩余的优惠券优惠总额
                surplusCouponPrice = CurrencyUtil.sub(surplusCouponPrice, couponRatioPrice);
            }

            updateRefundPrice(orderSn, orderSkuVO, refundPrice);
        }

        updateCouponItemRefundPrice(orderSn, noFmList, couponPrice, allTotal, noFmNum, surplusCouponPrice);
    }


    /**
     * 修改订单项的退款金额
     * 调用说明：1.针对订单一部分商品参与了满减促销活动，一部分商品没有参与满减促销活动并且还使用了优惠券的情况
     * 2.针对订单商品全部没有参与满减活动并且使用了优惠券的情况
     *
     * @param orderSn            订单编号
     * @param noFmList           没有参与满减促销活动的订单商品集合
     * @param couponPrice        订单使用的优惠券优惠金额
     * @param allTotal           所有商品的金额总计（还未减去满减优惠和优惠券优惠的金额总计）
     * @param noFmNum            获取未参与满减活动商品总数-1的数值（为了兼容金额比例无法整除而导致多个商品的退款金额总和与退款金额不一致的问题）
     * @param surplusCouponPrice 剩余的订单优惠券优惠总额
     */
    private void updateCouponItemRefundPrice(String orderSn, List<OrderSkuVO> noFmList, double couponPrice, double allTotal, int noFmNum, double surplusCouponPrice) {
        for (int i = 0; i < noFmList.size(); i++) {
            OrderSkuVO orderSkuVO = noFmList.get(i);
            double refundPrice = 0.00;

            if (i != noFmNum) {
                //获取当前商品优惠券优惠的占比金额
                double couponRatioPrice = CurrencyUtil.mul(CurrencyUtil.div(orderSkuVO.getSubtotal(), allTotal, 4), couponPrice);
                //当前商品应退款金额=金额总计-优惠券优惠的占比金额
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), couponRatioPrice);
                //计算剩余的优惠券优惠总额
                surplusCouponPrice = CurrencyUtil.sub(surplusCouponPrice, couponRatioPrice);
            } else {
                //当前商品应退款金额=金额总计-计算剩余的优惠券优惠总额
                refundPrice = CurrencyUtil.sub(orderSkuVO.getSubtotal(), surplusCouponPrice);
            }

            updateRefundPrice(orderSn, orderSkuVO, refundPrice);
        }
    }

    /**
     * 修改订单项可退款金额的公共方法
     *
     * @param orderSn     订单编号
     * @param orderSkuVO  订单商品明细
     * @param refundPrice 可退款金额
     */
    private void updateRefundPrice(String orderSn, OrderSkuVO orderSkuVO, double refundPrice) {
        String sql = "update es_order_items set refund_price = ? where order_sn = ? and goods_id = ? and product_id = ?";
        this.daoSupport.execute(sql, refundPrice, orderSn, orderSkuVO.getGoodsId(), orderSkuVO.getSkuId());
    }

    /**
     * 获取计算方式
     *
     * @param activityList   订单参与满减活动的商品集合
     * @param noActivityList 订单未参与满减活动的商品集合
     * @param couponPrice    订单使用优惠券优惠的金额
     * @return
     */
    private int getCountType(List<OrderSkuVO> activityList, List<OrderSkuVO> noActivityList, Double couponPrice) {
        if (activityList.size() == 0) {
            if (couponPrice == 0) {
                return 1;
            } else {
                return 2;
            }
        } else {
            if (couponPrice == 0) {
                if (noActivityList.size() == 0) {
                    return 3;
                } else {
                    return 4;
                }
            } else {
                if (noActivityList.size() == 0) {
                    return 5;
                } else {
                    return 6;
                }
            }
        }
    }

    /**
     * 对要操作的订单进行权限检查
     *
     * @param permission 需要的权限
     * @param order      相应的订单
     */
    private void checkPermission(OrderPermission permission, OrderDetailVO order) {

        if (permission != null) {
            if (order == null) {
                throw new NoPermissionException("无权操作此订单");
            }

            // 校验卖家权限
            if (permission.equals(OrderPermission.seller)) {
                Seller seller = UserContext.getSeller();
                if (seller == null || seller.getSellerId() != order.getSellerId().intValue()) {
                    throw new NoPermissionException("无权操作此订单");
                }
            }

            // 校验买家权限
            if (permission.equals(OrderPermission.buyer)) {
                Buyer buyer = UserContext.getBuyer();
                if (buyer == null || buyer.getUid() == null
                        || buyer.getUid().intValue() != order.getMemberId().intValue()) {
                    throw new NoPermissionException("无权操作此订单");
                }
            }

            // 校验管理权限
            if (permission.equals(OrderPermission.admin)) {

            }

            // 目前客户端不用校栓任何权限
            if (permission.equals(OrderPermission.client)) {

            }

        }
    }


    /**
     * 进行可操作校验
     * 看此状态下是否允许此操作
     *
     * @param order
     * @param orderOperate
     */
    private void checkAllowable(OrderPermission permission, OrderDetailVO order, OrderOperateEnum orderOperate) {
        //如果是client权限，则不验证下一步操作
        if (OrderPermission.client.equals(permission)) {
            return;
        }

        OrderStatusEnum status = OrderStatusEnum.valueOf(order.getOrderStatus());

        PaymentTypeEnum paymentType = PaymentTypeEnum.valueOf(order.getPaymentType());

        Map<OrderStatusEnum, OrderStep> flow = OrderOperateFlow.getFlow(paymentType, OrderTypeEnum.valueOf(order.getOrderType()));
        OrderOperateChecker orderOperateChecker = new OrderOperateChecker(flow);

        boolean isAllowble = orderOperateChecker.checkAllowable(status, orderOperate);

        if (!isAllowble) {
            throw new ServiceException(TradeErrorCode.E460.code(), "订单" + status.description() + "状态不能进行" + orderOperate.description() + "操作");
        }

    }


    /**
     * 内部类，为了传递参数使用
     */
    private class PayParam {
        private Double payPrice;
        private String returnTradeNo;

        public Double getPayPrice() {
            return payPrice;
        }

        public String getReturnTradeNo() {
            return returnTradeNo;
        }

        public void setPayPrice(Double payPrice) {
            this.payPrice = payPrice;
        }

        public void setReturnTradeNo(String returnTradeNo) {
            this.returnTradeNo = returnTradeNo;
        }
    }


}
